﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using Plugins.Core;
using Plugins.Library;

namespace Plugins.Controls
{
    #region Public Enums
    public enum LinearGradientAngleModeType
    {
        Horizontal = 0,
        PrimaryDiagonal = 45,
        Vertical = 90,
        SecondaryDiagonal = 135,
        BackwardHorizontal = 180,
        BackwardPrimaryDiagonal = 225,
        BackwardVertical = 270,
        BackwardSecondaryDiagonal = 315
    }
    public enum ProgressPositionSyncType
    {
        Global,
        Buffer,
        Manual
    }
    public enum GradientColorThemeType
    {
        DarkRed,
        DarkGreen,
        DarkBlue,
        DarkYellow,
        DarkOrchid,
        DarkOrange,
        DarkTomato,
        WhiteSmoke,
        WhiteBlue,
        WhiteGray,
        WhiteRed,
        WhiteGreen,
        WhiteOrange,
        WhiteBlack,
        White,
        Black,
        Yellow,
        Red,
        Green,
        Blue,
        Transparent
    }
    public enum ProgressBarType
    {
        Horisontal,
        Vertical,
        BackvardHorisontal,
        BackwardVertical
    }
    public enum GraphOrientationType
    {
        /// <summary>
        /// Not used yet
        /// </summary>
        Horisontal,
        /// <summary>
        /// Not used yet
        /// </summary>
        Vertical
    }
    public enum GraphDirectionType
    {
        Forward,
        Backward,
        Bidirectional
    }
    public enum GraphVisualizationType
    {
        Normal,
        Approximate
    }
    public enum DisplayStyle
    {
        Rectangular,
        Linear
    }
    #endregion
    [DefaultEvent("Click"), DefaultProperty("TextCaption")]
    public class PluginGradientControl : UserControl
    {
        #region Private Members
        private static object _sync = new object();
        private Image _logo;
        private Color _logoTransparentColor = Color.Black;
        private GraphDirectionType mGraphDirection = GraphDirectionType.Forward;
        private GraphVisualizationType mGraphVisualization = GraphVisualizationType.Normal;
        private DisplayStyle mDisplayStyle = DisplayStyle.Linear;
        private PluginDequeueCollection<double>[] mGraphBuffer;
        private double[] mValue;
        private double[] mManualValue;
        private double[] mBufferAverageValue;
        private double[] mGlobalAverageValue; 
        private double[] mLocalMax;
        private double[] mGraphOpacity;
        private double mProgressBarOpacity = 1.0;
        private double mMin = 0;
        private double mMax = 100;
        private string mInnerText = string.Empty;
        private bool mKeyPressed = false;
        private bool mMousePressed = false;
        private bool mFocused = false;
        private bool mMouseClicksEnabled = false;
        private bool mMousePressureEnabled = false;
        private bool mKeyClicksEnabled = false;
        private bool mShowFocus = false;
        private bool mShowInverted = false;
        private bool mShowMaximum = false;
        private bool mShowProgressBar = false;
        private bool mShowGraph = false;
        private bool mShowGraphAverage = false;
        private ProgressPositionSyncType mProgressPositionSync = ProgressPositionSyncType.Manual;
        private int mGlobalAverageCounter = 0;
        private int mGraphBufferLength = 1;
        private int mBufferLength = 64;
        private int mGraphCount = 1;
        private int mBorderWidth = 1;
        private int mProgressMaximumWidth = 1;
        private ProgressBarType mProgressBarType = ProgressBarType.Horisontal;
        private Color mGradientBorder = Color.Black;
        private Color mGraphColor = Color.Black;
        private Color mGraphAverageColor = Color.Red;
        private GradientColorThemeType mGradientColorTheme = GradientColorThemeType.WhiteGray;
        private GradientColorThemeType mProgressBarMaximumColorTheme = GradientColorThemeType.DarkRed;
        private GradientColorThemeType mProgressBarColorTheme = GradientColorThemeType.DarkRed;
        private GradientColorThemeType[] mGraphColorTheme;
        private Color mGradientBegin = Color.White;
        private Color mGradientEnd = Color.LightGray;
        private Color mProgressBarColorBegin = Color.Red;
        private Color mProgressBarColorEnd = Color.DarkRed;
        private Color mProgressMaximumColorBegin = Color.Red;
        private Color mProgressMaximumColorEnd = Color.DarkRed;
        private Color mGraphColorBegin = Color.Orange;
        private Color mGraphColorEnd = Color.Crimson;
        private SmoothingMode mGraphSmoothingMode = SmoothingMode.Default;
        private LinearGradientAngleModeType mLinearGradientMode = LinearGradientAngleModeType.Vertical;
        private StringFormatFlags mInnerFormat = StringFormatFlags.LineLimit;
        private Label mText;
        #endregion
        #region Constructor/Destructor
        public PluginGradientControl()
        {
            InitializeComponent();

            mText.MouseDown += GradientPanelControl_MouseDown;
            mText.MouseUp += GradientPanelControl_MouseUp;


            Initialize();
            InitializeGraphBuffer();

            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        }

        [Category("My")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public PluginDequeueCollection<double> Data 
        {
            get
            {
                return mGraphBuffer[0];
            }
            set
            {
                if (value != null)
                {
                    InitializeGraphBuffer(0, value);
                }
            }
        }
        //private void Initialize()
        //{
        //    if (mGraphCount > 0 && mGraphBufferLength > 0)
        //    {
        //        mGraphBuffer = new PluginDequeueCollection<double>[mGraphCount];
        //        mValue = new double[mGraphCount];
        //        mManualValue = new double[mGraphCount];
        //        mBufferAverageValue = new double[mGraphCount];
        //        mGlobalAverageValue = new double[mGraphCount];
        //        mLocalMax = new double[mGraphCount];
        //        mGraphOpacity = new double[mGraphCount];
        //        for (int j = 0; j < mGraphCount; j++)
        //        {
        //            mGraphBuffer[j] = new PluginDequeueCollection<double>(mGraphBufferLength);
        //            SetGraphBuffer(j);
        //            mValue[j] = 0;
        //            mManualValue[j] = 0;
        //            mBufferAverageValue[j] = 0;
        //            mGlobalAverageValue[j] = 0.0;
        //            mLocalMax[j] = 0;
        //            mGraphOpacity[j] = 1.0;
        //        }
        //        mGlobalAverageCounter = 0;
        //    }
        //}
        private void InitializeGraphBuffer()
        {
            if (mGraphCount > 0 && mGraphBufferLength > 0)
            {
                mGraphBuffer = new PluginDequeueCollection<double>[mGraphCount];
                for (int j = 0; j < mGraphCount; j++)
                {
                    mGraphBuffer[j] = new PluginDequeueCollection<double>();
                    SetGraphBuffer(j);
                }
            }
        }
        private void InitializeGraphBuffer(PluginDequeueCollection<double> _mGraphBuffer)
        {
            if (mGraphCount > 0 && mGraphBufferLength > 0)
            {
                for (int j = 0; j < mGraphCount; j++)
                {
                    mGraphBuffer[j] = new PluginDequeueCollection<double>(_mGraphBuffer);
                    SetGraphBuffer(j);
                }
            }
        }
        private void InitializeGraphBuffer(int index, PluginDequeueCollection<double> _mGraphBuffer)
        {
            if (mGraphCount > index && index >= 0 && mGraphBufferLength > 0)
            {
                mGraphBuffer[index] = _mGraphBuffer;
                SetGraphBuffer(index);
            }
        }
        private void Initialize()
        {
            if (mGraphCount > 0 && mGraphBufferLength > 0)
            {
                mGraphColorTheme = new GradientColorThemeType[mGraphCount];
                mValue = new double[mGraphCount];
                mManualValue = new double[mGraphCount];
                mBufferAverageValue = new double[mGraphCount];
                mGlobalAverageValue = new double[mGraphCount];
                mLocalMax = new double[mGraphCount];
                mGraphOpacity = new double[mGraphCount];
                for (int j = 0; j < mGraphCount; j++)
                {
                    mValue[j] = 0;
                    mManualValue[j] = 0;
                    mBufferAverageValue[j] = 0;
                    mGlobalAverageValue[j] = 0.0;
                    mLocalMax[j] = 0;
                    mGraphOpacity[j] = 1.0;
                }
                mGlobalAverageCounter = 0;
            }
        }
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }
        #endregion
        #region Public Properties
        [Category("My"), DefaultValue(typeof(GraphDirectionType), "Forward")]
        public GraphDirectionType GrapthDirection
        {
            get
            {
                return mGraphDirection;
            }
            set
            {
                mGraphDirection = value;
                UpdateGraphics();
            }
        }

        private void UpdateGraphics()
        {
            if (_canUpdate)
            {
                Invalidate();
            }
        }
        [Category("My"), DefaultValue(typeof(SmoothingMode), "Default")]
        public SmoothingMode GraphSmoothingMode
        {
            get
            {
                return mGraphSmoothingMode;
            }
            set
            {
                if (value == SmoothingMode.Invalid)
                {
                    mGraphSmoothingMode = SmoothingMode.Default;
                    UpdateGraphics();
                    return;
                }
                mGraphSmoothingMode = value;
                UpdateGraphics();
            }
        }
        [Category("My"), DefaultValue(typeof(DisplayStyle), "Linear")]
        public DisplayStyle DisplayStyle
        {
            get
            {
                return mDisplayStyle;
            }
            set
            {
                mDisplayStyle = value;
                UpdateGraphics();
            }
        }
        [Category("My"), DefaultValue(1.0), TypeConverter(typeof(OpacityConverter))]
        public double ProgressBarOpacity
        {
            get
            {
                return mProgressBarOpacity;
            }
            set
            {
                if (value < 0.0 || value > 1.0)
                {
                    return;
                }
                mProgressBarOpacity = value;
                UpdateGraphics();
            }
        }
        [Category("My")]
        public double[] GraphOpacity
        {
            get
            {
                return mGraphOpacity;
            }
            set
            {
                if (value != null)
                {
                    if (value.Length == mGraphCount)
                    {
                        for (int i = 0; i < value.Length; i++)
                        {
                            if (value[i] < 0.0 || value[i] > 1.0)
                            {
                                throw new ArgumentOutOfRangeException();
                            }
                            mGraphOpacity[i] = value[i];
                        }
                        UpdateGraphics();
                        return;
                    }
                    else
                    {
                        GraphCount = value.Length;
                        for (int i = 0; i < value.Length; i++)
                        {
                            if (value[i] < 0.0 || value[i] > 1.0)
                            {
                                throw new ArgumentOutOfRangeException();
                            }
                            mGraphOpacity[i] = value[i];
                        }
                    }
                    UpdateGraphics();
                }
            }
        }
        private bool _useAutovalues;
        [Category("My"), DefaultValue(typeof(bool), "false")]
        public bool AutoValues
        {
            get
            {
                return _useAutovalues;
            }
            set             
            {
                if (_useAutovalues != value) 
                {
                    _useAutovalues = value;
                }
            }
        }
        [Category("My"), DefaultValue(typeof(int), "1")]
        public int GraphCount
        {
            get
            {
                return mGraphCount;
            }
            set
            {
                if (mGraphCount != value)
                {
                    mGraphCount = value;
                    Initialize();
                    InitializeGraphBuffer();
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(ContentAlignment.TopLeft)]
        public ContentAlignment TextAlign
        {
            get
            {
                return mText.TextAlign;
            }
            set
            {
                mText.TextAlign = value;
                UpdateGraphics();
            }
        }
        [Category("My"), DefaultValue(typeof(GradientColorThemeType), "WhiteGray")]
        public GradientColorThemeType Theme
        {
            get
            {
                return mGradientColorTheme;
            }
            set
            {
                if (mGradientColorTheme != value)
                {
                    mGradientColorTheme = value;
                    SetColorTheme(mGradientColorTheme, out mGradientBegin, out mGradientEnd);
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(typeof(GradientColorThemeType), "DarkRed")]
        public GradientColorThemeType ProgressTheme
        {
            get
            {
                return mProgressBarColorTheme;
            }
            set
            {
                if (mProgressBarColorTheme != value)
                {
                    mProgressBarColorTheme = value;
                    SetColorTheme(mProgressBarColorTheme, out mProgressBarColorBegin, out mProgressBarColorEnd);
                    UpdateGraphics();
                }
            }
        }
        [Category("My")]
        [DefaultValue(null)]
        public GradientColorThemeType[] GraphThemes
        {
            get
            {
                return mGraphColorTheme;
            }
            set
            {
                if (value != null)
                {
                    mGraphColorTheme = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(typeof(GradientColorThemeType), "DarkRed")]
        public GradientColorThemeType MaximumTheme
        {
            get
            {
                return mProgressBarMaximumColorTheme;
            }
            set
            {
                if (mProgressBarMaximumColorTheme != value)
                {
                    mProgressBarMaximumColorTheme = value;
                    SetColorTheme(mProgressBarMaximumColorTheme, out mProgressMaximumColorBegin, out mProgressMaximumColorEnd);
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue("")]
        public string TextCaption
        {
            get
            {
                return mInnerText;
            }
            set
            {
                if (value != null)
                {
                    if (mInnerText != value)
                    {
                        mInnerText = value;
                        mText.Text = mInnerText;
                        UpdateGraphics();
                    }
                }
            }
        }
        [Category("My"), DefaultValue(1)]
        public int BorderWidth
        {
            get
            {
                return mBorderWidth;
            }
            set
            {
                if (mBorderWidth != value)
                {
                    mBorderWidth = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(1)]
        public int MaximumWidth
        {
            get
            {
                return mProgressMaximumWidth;
            }
            set
            {
                if (mProgressMaximumWidth != value)
                {
                    mProgressMaximumWidth = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(typeof(Color), "Black")]
        public Color BorderColor
        {
            get
            {
                return mGradientBorder;
            }
            set
            {
                if (mGradientBorder != value)
                {
                    mGradientBorder = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(LinearGradientAngleModeType.Vertical)]
        public LinearGradientAngleModeType GradientMode
        {
            get
            {
                return mLinearGradientMode;
            }
            set
            {
                if (mLinearGradientMode != value)
                {
                    mLinearGradientMode = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(typeof(Color), "ControlText")]
        public Color TextColor
        {
            get
            {
                return mText.ForeColor;
            }
            set
            {
                if (mText.ForeColor != value)
                {
                    mText.ForeColor = value;
                    if (mColorChanged != null)
                    {
                        mColorChanged(value);
                    }
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(StringFormatFlags.LineLimit)]
        public StringFormatFlags TextFormat
        {
            get
            {
                return mInnerFormat;
            }
            set
            {
                if (mInnerFormat != value)
                {
                    mInnerFormat = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(false)]
        public bool EnableMouseClicks
        {
            get
            {
                return mMouseClicksEnabled;
            }
            set
            {
                if (mMouseClicksEnabled != value)
                {
                    mMouseClicksEnabled = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(false)]
        public bool EnableMousePressure
        {
            get
            {
                return mMousePressureEnabled;
            }
            set
            {
                if (mMousePressureEnabled != value)
                {
                    mMousePressureEnabled = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(false)]
        public bool EnableKeyClicks
        {
            get
            {
                return mKeyClicksEnabled;
            }
            set
            {
                if (mKeyClicksEnabled != value)
                {
                    mKeyClicksEnabled = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(false)]
        public bool EnablePanelFocus
        {
            get
            {
                return mShowFocus;
            }
            set
            {
                if (mShowFocus != value)
                {
                    mShowFocus = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(false)]
        public bool EnablePanelFocusInverted
        {
            get
            {
                return mShowInverted;
            }
            set
            {
                if (mShowInverted != value)
                {
                    mShowInverted = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(typeof(double), "0.0")]
        public double Min
        {
            get
            {
                return mMin;
            }
            set
            {
                if (mMax <= value)
                {
                    throw new ArgumentOutOfRangeException();
                }
                if (mMin != value)
                {
                    mMin = value;
                    OnMinChanged(value);
                    //CreateGrapth();
                    UpdateGraphics();
                    return;
                }
            }
        }
        private void OnMinChanged(double value)
        {
            if (mMinChanged != null)
            {
                mMinChanged(value);
            }
        }
        [Category("My"), DefaultValue(typeof(double), "100.0")]
        public double Max
        {
            get
            {
                return mMax;
            }
            set
            {
                if (value <= mMin)
                {
                    throw new ArgumentOutOfRangeException();
                }
                if (mMax != value)
                {
                    mMax = value;
                    OnMaxChanged(mMax);
                    //CreateGrapth(); ???
                    UpdateGraphics();
                    return;
                }
            }
        }

        private void OnMaxChanged(double value)
        {
            if (mMaxChanged != null)
            {
                mMaxChanged(value);
            }
        }
        [Category("My"), DefaultValue(false)]
        public bool ShowMaximum
        {
            get
            {
                return mShowMaximum;
            }
            set
            {
                if (mShowMaximum != value)
                {
                    mShowMaximum = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(false)]
        public bool ShowProgressBar
        {
            get
            {
                return mShowProgressBar;
            }
            set
            {
                if (mShowProgressBar != value)
                {
                    mShowProgressBar = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(0)]
        public double ProgressPosition
        {
            get
            {
                return GetProgressBarValue(0);
            }
            set
            {
                SetBufferedValue(0, value);
                SetProgressBarValue(0, value);
                UpdateGraphics();
            }
        }
        [Category("My"), DefaultValue(typeof(int), "64")]
        public int TextBufferLength
        {
            get
            {
                return mBufferLength;
            }
            set
            {
                if (mBufferLength != value)
                {
                    mBufferLength = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(ProgressBarType.Horisontal)]
        public ProgressBarType ProgressType
        {
            get
            {
                return mProgressBarType;
            }
            set
            {
                if (mProgressBarType != value)
                {
                    mProgressBarType = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(false)]
        public bool ShowProgressGraph
        {
            get
            {
                return mShowGraph;
            }
            set
            {
                if (mShowGraph != value)
                {
                    mShowGraph = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(false)]
        public bool ShowProgressGraphAverage
        {
            get
            {
                return mShowGraphAverage;
            }
            set
            {
                if (mShowGraphAverage != value)
                {
                    mShowGraphAverage = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(typeof(int), "1")]
        public int GrapthBufferLength
        {
            get
            {
                return mGraphBufferLength;
            }
            set
            {
                if (mGraphBufferLength != value)
                {
                    mGraphBufferLength = value;
                    InitializeGraphBuffer(new PluginDequeueCollection<double>(value));
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(typeof(Color), "Black")]
        public Color GrapthColor
        {
            get
            {
                return mGraphColor;
            }
            set
            {
                if (mGraphColor != value)
                {
                    mGraphColor = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(typeof(Color), "Red")]
        public Color GrapthAverageColor
        {
            get
            {
                return mGraphAverageColor;
            }
            set
            {
                if (mGraphAverageColor != value)
                {
                    mGraphAverageColor = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(typeof(ProgressPositionSyncType), "Manual")]
        public ProgressPositionSyncType PositionAverageSync
        {
            get
            {
                return mProgressPositionSync;
            }
            set
            {
                if (mProgressPositionSync != value)
                {
                    mProgressPositionSync = value;
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(typeof(GraphVisualizationType), "Normal")]
        public GraphVisualizationType GraphVisualization
        {
            get
            {
                return mGraphVisualization;
            }
            set
            {
                if (mGraphVisualization != value)
                {
                    mGraphVisualization = value;
                    UpdateGraphics();
                }
            }
        }
        #endregion
        #region Component Core
        private RectangleF GetProgressBarRectangle(int offset)
        {
            switch (mProgressBarType)
            {
                case ProgressBarType.Horisontal:
                    return new RectangleF(0, 0, offset, ClientSize.Height);
                case ProgressBarType.BackvardHorisontal:
                    return new RectangleF(offset, 0, ClientSize.Width, ClientSize.Height);
                case ProgressBarType.Vertical:
                    return new RectangleF(0, offset, ClientSize.Width, ClientSize.Height);
                case ProgressBarType.BackwardVertical:
                    return new RectangleF(0, 0, ClientSize.Width, offset);
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
        private PointF[] GetPoints(int j)
        {
            PointF[] points = null;
            switch (mDisplayStyle)
            {
                case DisplayStyle.Rectangular:
                    {
                        points = new PointF[mGraphBuffer[j].Count * 2 + 2];
                        if (mGraphBuffer[j].Count > 1)
                        {
                            double sx = 0;
                            double dx = (double)((ClientSize.Width/*-1*/) / (double)(mGraphBuffer[j].Count/*-1*/));
                            points[0] = new PointF(0, ClientSize.Height/*-1*/);
                            for (int i = 0; i < mGraphBuffer[j].Count; i++)
                            {
                                points[2 * i + 1].X = (float)dx * i;
                                points[2 * i + 1].Y = (float)(((double)(ClientSize.Height/*-1*/)) * ((mMax - GetBufferedValue(j, i)) / (mMax - mMin)));
                                points[2 * i + 2].X = (float)(points[2 * i + 1].X + dx);
                                points[2 * i + 2].Y = points[2 * i + 1].Y;
                                sx += (double)GetBufferedValue(j, i);
                            }
                            points[2 * mGraphBuffer[j].Count + 1] = new PointF(ClientSize.Width/*-1*/, ClientSize.Height/*-1*/);
                            mBufferAverageValue[j] = sx / (double)mGraphBuffer[j].Count;
                        }
                        return points;
                    }
                case DisplayStyle.Linear:
                    points = new PointF[mGraphBuffer[j].Count + 2];
                    if (mGraphBuffer[j].Count > 1)
                    {
                        double sx = 0;
                        double dx = (double)((ClientSize.Width/*-1*/) / (double)(mGraphBuffer[j].Count - 1));
                        points[0] = new PointF(0, ClientSize.Height/*-1*/);
                        for (int i = 0; i < mGraphBuffer[j].Count; i++)
                        {
                            points[i + 1].X = (float)dx * i;
                            points[i + 1].Y = (float)(((double)(ClientSize.Height/*-1*/)) * ((mMax - GetBufferedValue(j, i)) / (mMax - mMin)));
                            sx += (double)GetBufferedValue(j, i);
                        }
                        points[mGraphBuffer[j].Count + 1] = new PointF(ClientSize.Width/*-1*/, ClientSize.Height/*-1*/);
                        mBufferAverageValue[j] = sx / (double)mGraphBuffer[j].Count; // PASSED 
                    }
                    return points;
                default:
                    return null;
            }
        }
        private void SetGraphBuffer(int index)
        {
            mGraphBuffer[index].Clear();
            for (int i = 0; i < mGraphBufferLength; i++)
            {
                mGraphBuffer[index].Add(mMin);
            }
        }
        private void SetColorTheme(GradientColorThemeType theme, out Color gradientBegin, out Color gradientEnd)
        {
            switch (theme)
            {
                case GradientColorThemeType.DarkRed:
                    gradientBegin = Color.Red;
                    gradientEnd = Color.DarkRed;
                    break;
                case GradientColorThemeType.DarkGreen:
                    gradientBegin = Color.Lime;
                    gradientEnd = Color.DarkGreen;
                    break;
                case GradientColorThemeType.DarkBlue:
                    gradientBegin = Color.LightSkyBlue;
                    gradientEnd = Color.DarkBlue;
                    break;
                case GradientColorThemeType.DarkYellow:
                    gradientBegin = Color.Yellow;
                    gradientEnd = Color.DarkKhaki;
                    break;
                case GradientColorThemeType.DarkOrchid:
                    gradientBegin = Color.Orchid;
                    gradientEnd = Color.DarkOrchid;
                    break;
                case GradientColorThemeType.DarkOrange:
                    gradientBegin = Color.Orange;
                    gradientEnd = Color.Crimson;
                    break;
                case GradientColorThemeType.DarkTomato:
                    gradientBegin = Color.Yellow;
                    gradientEnd = Color.Tomato;
                    break;
                case GradientColorThemeType.WhiteSmoke:
                    gradientBegin = Color.WhiteSmoke;
                    gradientEnd = Color.LightGray;
                    break;
                case GradientColorThemeType.WhiteBlue:
                    gradientBegin = Color.White;
                    gradientEnd = Color.Blue;
                    break;
                case GradientColorThemeType.WhiteGray:
                    gradientBegin = Color.White;
                    gradientEnd = Color.LightGray;
                    break;
                case GradientColorThemeType.WhiteRed:
                    gradientBegin = Color.White;
                    gradientEnd = Color.Red;
                    break;
                case GradientColorThemeType.WhiteGreen:
                    gradientBegin = Color.White;
                    gradientEnd = Color.LimeGreen;
                    break;
                case GradientColorThemeType.WhiteOrange:
                    gradientBegin = Color.White;
                    gradientEnd = Color.Orange;
                    break;
                case GradientColorThemeType.WhiteBlack:
                    gradientBegin = Color.White;
                    gradientEnd = Color.Black;
                    break;
                case GradientColorThemeType.White:
                    gradientBegin = Color.White;
                    gradientEnd = Color.White;
                    break;
                case GradientColorThemeType.Black:
                    gradientBegin = Color.Black;
                    gradientEnd = Color.Black;
                    break;
                case GradientColorThemeType.Yellow:
                    gradientBegin = Color.Yellow;
                    gradientEnd = Color.Yellow;
                    break;
                case GradientColorThemeType.Red:
                    gradientBegin = Color.Red;
                    gradientEnd = Color.Red;
                    break;
                case GradientColorThemeType.Green:
                    gradientBegin = Color.Green;
                    gradientEnd = Color.Green;
                    break;
                case GradientColorThemeType.Blue:
                    gradientBegin = Color.Blue;
                    gradientEnd = Color.Blue;
                    break;
                default:
                    gradientBegin = Color.Transparent;
                    gradientEnd = Color.Transparent;
                    break;
            }
        }
        public double GetMaximumValue(int index)
        {
            return mLocalMax[index];
        }
        public double GetAveragePosition(int index)
        {
            if (index >= mGraphCount)
            {
                throw new ArgumentOutOfRangeException();
            }
            return mBufferAverageValue[index];
        }
        public double GetGlobalAveragePosition(int index)
        {
            if (index >= mGraphCount)
            {
                throw new ArgumentOutOfRangeException();
            }
            return mGlobalAverageValue[index];
        }
        public double GetAverageValue(int index)
        {
            switch (mProgressPositionSync)
            {
                case ProgressPositionSyncType.Buffer:
                    return mBufferAverageValue[index];
                case ProgressPositionSyncType.Global:
                    return mGlobalAverageValue[index]; 
                case ProgressPositionSyncType.Manual:
                    return mManualValue[index];
                default:
                    return 0.0;
            }
        }
        public double GetProgressBarValue(int index)
        {
            return mValue[index];
        }
        public double GetGraphValue(double value)
        {
            double length;
            double delta;
            switch (mProgressBarType)
            {
                case ProgressBarType.Horisontal:
                    delta = (value - mMin) / (mMax - mMin);
                    length = ClientSize.Width;
                    break;
                case ProgressBarType.Vertical:
                    delta = (mMax - value) / (mMax - mMin);
                    length = ClientSize.Height;
                    break;
                case ProgressBarType.BackvardHorisontal:
                    delta = (mMax - value) / (mMax - mMin);
                    length = ClientSize.Width;
                    break;
                case ProgressBarType.BackwardVertical:
                    delta = (value - mMin) / (mMax - mMin);
                    length = ClientSize.Height;
                    break;
                default:
                    return 0.0;
            }
            return length * delta;
        }
        public double GetBufferedValue(int index, int position)
        {
            double sx = 0;
            for (int i = 0; i < mGraphCount - index; i++)
            {
                sx += (double)mGraphBuffer[i][position];
            }
            return sx;
        }
        private void DrawProgressBar(Graphics g, int j, double angle)
        {
            Color begin;
            Color end;
            SetAverageValue(j);
            GetOpacityColors(mProgressBarColorBegin, mProgressBarColorEnd, mProgressBarOpacity, out begin, out end);
            LinearGradientBrush brush = new LinearGradientBrush(ClientRectangle, begin, end, (float)angle);
            g.FillRectangle(brush, GetProgressBarRectangle((int)GetGraphValue(GetProgressBarValue(j))));
        }
        private void DrawMaximum(Graphics g, int j, double angle)
        {
            Color begin;
            Color end;
            SetAverageValue(j);
            GetOpacityColors(mProgressMaximumColorBegin, mProgressMaximumColorEnd, mGraphOpacity[j], out begin, out end);
            LinearGradientBrush brush = new LinearGradientBrush(ClientRectangle, begin, end, (float)angle);
            DrawMaximumLine(g, brush, (int)GetGraphValue(GetMaximumValue(j)));
        }
        private void DrawMaximumLine(Graphics g, LinearGradientBrush brush, int offset)
        {
            switch (mProgressBarType)
            {
                case ProgressBarType.Horisontal:
                case ProgressBarType.BackvardHorisontal:
                    g.DrawLine(new Pen(brush, mProgressMaximumWidth), offset, 0, offset, ClientSize.Height);
                    break;
                case ProgressBarType.Vertical:
                case ProgressBarType.BackwardVertical:
                    g.DrawLine(new Pen(brush, mProgressMaximumWidth), 0, offset, ClientSize.Width, offset);
                    break;
            }
        }
        private void DrawGraphAverage(Graphics g, int j, double angle)
        {
            SetAverageValue(j);
            DrawLine(g, mGraphAverageColor, (int)GetGraphValue(GetProgressBarValue/*GetAverage*/(j)));
        }
        private void DrawLine(Graphics g, Color color, int offset)
        {
            switch (mProgressBarType)
            {
                case ProgressBarType.Horisontal:
                case ProgressBarType.BackvardHorisontal:
                    g.DrawLine(new Pen(color), offset, 0, offset, ClientSize.Height);
                    break;
                case ProgressBarType.Vertical:
                case ProgressBarType.BackwardVertical:
                    g.DrawLine(new Pen(color), 0, offset, ClientSize.Width, offset);
                    break;
                default:
                    break;
            }
        }
        private void DrawGraph(Graphics g, PointF[] points, int j, int angle)
        {
            Color begin;
            Color end;
            if (mGraphBuffer[j].Count > 1)
            {
                GraphicsPath path = new GraphicsPath();
                path.AddLines(points);
                if (mGraphColorTheme.Length == mGraphCount)
                {
                    SetColorTheme(mGraphColorTheme[j], out mGraphColorBegin, out mGraphColorEnd);
                }
                SetAverageValue(j);
                GetOpacityColors(mGraphColorBegin, mGraphColorEnd, mGraphOpacity[j], out begin, out end);
                g.FillPath(new LinearGradientBrush(ClientRectangle, begin, end, (float)angle), path);
                g.DrawLines(new Pen(mGraphColor), path.PathPoints);
                g.DrawLine(new Pen(mGraphColor), 0, ClientSize.Height/*-1*/, ClientSize.Width/*-1*/, ClientSize.Height/*-1*/);
            }
        }
        private Bitmap _logoBitmap;
        [Category("My"), DefaultValue(typeof(Image), "null")]
        public Image Image
        {
            get
            {
                return _logo;
            }
            set
            {
                if (_logo != value)
                {
                    _logo = value;
                    UpdateBitmap(_logo, _logoTransparentColor);
                    UpdateGraphics();
                }
            }
        }
        [Category("My"), DefaultValue(typeof(Color), "Black")]
        public Color TransparentColor
        {
            get
            {
                return _logoTransparentColor;
            }
            set
            {
                if (_logoTransparentColor != value)
                {
                    _logoTransparentColor = value;
                    UpdateBitmap(_logo, _logoTransparentColor);
                    UpdateGraphics();
                }
            }
        }
        private void UpdateBitmap(Image image, Color color)
        {
            if (image != null)
            {
                // BUF FIX: Now, it's up to user to implement alpha blending, due to the bottleneck in source code
                //_logoBitmap = PluginBitmapHelper.GetTransparentBitmap(image, color);
                _logoBitmap = new Bitmap(image);
            }
            else
            {
                if (_logoBitmap != null)
                {
                    _logoBitmap.Dispose();
                    _logoBitmap = null;
                }
            }
        }

        private void Draw(PaintEventArgs e)
        {
            lock (_sync)
            {
                Graphics g = e.Graphics;
                g.SmoothingMode = mGraphSmoothingMode;
                if (ClientSize.Width > 0 && ClientSize.Height > 0)
                {
                    int angle = (int)mLinearGradientMode;
                    if ((mKeyPressed && mKeyClicksEnabled) || (mMousePressed && mMouseClicksEnabled))
                    {
                        angle = (angle + 180) % 360;
                    }
                    if (mShowInverted && mFocused)
                    {
                        angle = (angle + 180) % 360;
                    }
                    g.FillRectangle(new LinearGradientBrush(ClientRectangle, mGradientBegin, mGradientEnd, angle), ClientRectangle);
                    for (int j = 0; j < mGraphCount; j++)
                    {
                        PointF[] points = GetPoints(j);
                        if (points != null && points.Length > 0)
                        {
                            if (mShowGraph)
                            {
                                DrawGraph(g, points, j, angle);
                            }
                            if (mShowProgressBar)
                            {
                                DrawProgressBar(g, j, angle);
                            }
                            if (mShowGraphAverage)
                            {
                                DrawGraphAverage(g, j, angle);
                            }
                            if (mShowMaximum)
                            {
                                DrawMaximum(g, j, angle);
                            }
                        }
                    }
                    if (mShowFocus)
                    {
                        if (mFocused || (mMousePressed && mMouseClicksEnabled) || (mKeyPressed && mKeyClicksEnabled))
                        {
                            g.DrawRectangle(new Pen(mGradientBorder, mBorderWidth), new Rectangle(0, 0, ClientSize.Width - 1, ClientSize.Height - 1));
                        }
                    }
                    if (mMousePressureEnabled && mMousePressed)
                    {
                        mMousePressed = false;
                        OnMouseDown(null);
                    }
                    if (_logoBitmap != null)
                    {
                        using (Bitmap bitmap = new Bitmap(_logoBitmap))
                        {
                            CenterGraphics(g, GetClientRectangle(), _logoBitmap.Width, _logoBitmap.Height);
                            g.DrawImage(bitmap, 0, 0, bitmap.Width, bitmap.Height);
                        }
                    }
                }
            }
        }

        private RectangleF GetClientRectangle()
        {
            Rectangle client = this.ClientRectangle;
            RectangleF rect = new RectangleF();
            rect.Width = client.Width;
            rect.Height = client.Height;
            return rect;
        }

        private void CenterGraphics(Graphics g, RectangleF r, float width, float height)
        {
            var transformMatrix = new Matrix();
            transformMatrix.Translate(r.Left + r.Width / 2.0f - width / 2.0f, r.Top + r.Height / 2.0f - height / 2.0f);
            g.MultiplyTransform(transformMatrix, MatrixOrder.Append);
        }

        private void GetOpacityColors(Color c1, Color c2, double opacity, out Color begin, out Color end)
        {
            begin = Color.FromArgb((int)(255 * opacity), c1.R, c1.G, c1.B);
            end = Color.FromArgb((int)(255 * opacity), c2.R, c2.G, c2.B);
        }
        private void SetAverageValue(int j)
        {
            switch (mProgressPositionSync)
            {
                case ProgressPositionSyncType.Buffer:
                    SetProgressBarValue(j, mBufferAverageValue[j]);
                    break;
                case ProgressPositionSyncType.Global:
                    double sx = 0.0;
                    for (int i = 0; i < mGraphCount - j; i++)
                    {
                        sx += mGlobalAverageValue[i];
                    }
                    SetProgressBarValue(j, sx);
                    break;
                case ProgressPositionSyncType.Manual:
                    SetProgressBarValue(j, mManualValue[j]);
                    break;
                default:
                    break;
            }
        }
        private void SetProgressBarValue(int j, double value)
        {
            if (value < mMin || value > mMax)
            {
                return;
            }

            mValue[j] = value;

            if (mShowMaximum)
            {
                SetMaxValue(j, value, false);
            }
        }
        private void SetBufferedValue(int j, double value)
        {
            switch (mProgressPositionSync)
            {
                case ProgressPositionSyncType.Buffer:
                    mBufferAverageValue[j] = value;
                    break;
                case ProgressPositionSyncType.Global:
                    mGlobalAverageValue[j] = value; // PASSED 
                    break;
                case ProgressPositionSyncType.Manual:
                    mManualValue[j] = value;
                    break;
                default:
                    break;
            }
        }
        private void SetMaxValue(int index, double value, bool overwrite)
        {
            if (value > mLocalMax[index] || overwrite)
            {
                mLocalMax[index] = value;
            }
        }
        private void GradientPanelControl_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Draw(e);
        }
        private void SetText(string text)
        {
            if (text != null)
            {
                if (text.Length > mBufferLength)
                {
                    TextCaption = text.Substring(0, (int)mBufferLength);
                    return;
                }
                TextCaption = text;
            }
        }
        #endregion
        #region Private Events
        private event ColorChangedEventHandler mColorChanged;
        private event ValueChangedEventHandler mMaxChanged;
        private event ValueChangedEventHandler mMinChanged;
        private void GradientPanelControl_Enter(object sender, System.EventArgs e)
        {
            mFocused = true;
            UpdateGraphics();
        }
        private void GradientPanelControl_Leave(object sender, System.EventArgs e)
        {
            mFocused = false;
            mKeyPressed = false;
            mMousePressed = false;
            UpdateGraphics();
        }
        private void GradientPanelControl_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (!mMousePressed)
            {
                mMousePressed = true;
                UpdateGraphics();
            }
        }
        private void GradientPanelControl_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (mMousePressed)
            {
                mMousePressed = false;
                UpdateGraphics();
            }
        }
        private void GradientPanelControl_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            if (!mKeyPressed)
            {
                mKeyPressed = true;
                UpdateGraphics();
            }
        }
        private void GradientPanelControl_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            if (mKeyPressed)
            {
                mKeyPressed = false;
                UpdateGraphics();
            }
        }
        private void ControlClick(object sender, System.EventArgs e)
        {
            base.OnClick(e);
        }
        private void ControlMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            base.OnMouseDown(e);
        }
        #endregion
        #region Public Events
        public delegate void ColorChangedEventHandler(Color color);
        public delegate void ValueChangedEventHandler(double value);
        [Category("My")]
        public event ColorChangedEventHandler ColorChanged
        {
            add
            {
                mColorChanged += value;
            }
            remove
            {
                mColorChanged -= value;
            }
        }
        [Category("My")]
        public event ValueChangedEventHandler MinChanged
        {
            add
            {
                mMinChanged += value;
            }
            remove
            {
                mMinChanged -= value;
            }
        }
        [Category("My")]
        public event ValueChangedEventHandler MaxChanged
        {
            add
            {
                mMaxChanged += value;
            }
            remove
            {
                mMaxChanged -= value;
            }
        }
        #endregion
        public void ResetProgressBar()
        {
            for (int j = 0; j < mGraphCount; j++)
            {
                SetProgressBarValue(j, 0);
            }
            mGlobalAverageCounter = 0;
        }
        public void AddGrapthPoints(double[] points)
        {
            if (points != null)
            {
                if (points.Length == mGraphCount)
                {
                    for (int j = 0; j < mGraphCount; j++)
                    {
                        if (points[j] < mMin)
                        {
                            if (_useAutovalues)
                            {
                                mMin = points[j];
                                if (mMinChanged != null)
                                {
                                    mMinChanged(mMin);
                                }
                            }
                            else
                            {
                                points[j] = mMin;
                            }
                        }
                        if (points[j] > mMax)
                        {
                            if (_useAutovalues)
                            {
                                mMax = points[j];
                                OnMaxChanged(mMax);
                            }
                            else
                            {
                                points[j] = mMax;
                            }
                        }
                        SetGraphBufferValue(points, j);
                        mGlobalAverageValue[j] = (points[j] + (double)(mGlobalAverageCounter) * mGlobalAverageValue[j]) / ((double)(mGlobalAverageCounter + 1));
                    }
                    mGlobalAverageCounter++;
                    UpdateGraphics();
                }
            }
        }
        private void SetGraphBufferValue(double[] points, int j)
        {
            switch (mGraphDirection)
            {
                case GraphDirectionType.Forward:
                    mGraphBuffer[j].AddFirstLastOut(points[j], mGraphBufferLength);
                    PatchPoints(points, j);
                    break;
                case GraphDirectionType.Backward:
                    mGraphBuffer[j].AddLastFirstOut(points[j], mGraphBufferLength);
                    PatchPoints(points, j);
                    break;
                case GraphDirectionType.Bidirectional:
                    mGraphBuffer[j].AddFirstLastCenterOut(points[j], points[j]);
                    PatchPoints(points, j);
                    break;
                default:
                    break;
            }
        }
        private void PatchPoints(double[] points, int j)
        {
            switch (mGraphDirection)
            {
                case GraphDirectionType.Forward:
                    switch (mGraphVisualization)
                    {
                        case GraphVisualizationType.Normal:
                            break;
                        case GraphVisualizationType.Approximate:
                            ForwardApproximate(points, j, 1, (int)(mGraphBufferLength - 1));
                            break;
                        default:
                            break;
                    }
                    break;
                case GraphDirectionType.Backward:
                    switch (mGraphVisualization)
                    {
                        case GraphVisualizationType.Normal:
                            break;
                        case GraphVisualizationType.Approximate:
                            BackwardApproximate(points, j, (int)(mGraphBufferLength - 2), 0);
                            break;
                        default:
                            break;
                    }
                    break;
                case GraphDirectionType.Bidirectional:
                    switch (mGraphVisualization)
                    {
                        case GraphVisualizationType.Normal:
                            break;
                        case GraphVisualizationType.Approximate:
                            ForwardApproximate(points, j, 1, (int)(mGraphBufferLength / 2));
                            BackwardApproximate(points, j, (int)(mGraphBufferLength - 2), (int)(mGraphBufferLength / 2 - 1));
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
        }
        private void ForwardApproximate(double[] points, int j, int start, int end)
        {
            int n = 1;
            for (int i = start; i < end; i++, n++)
            {
                mGraphBuffer[j][i] = (mGraphBuffer[j][i] * n + points[j]) / (double)(n + 1);
            }
        }
        private void BackwardApproximate(double[] points, int j, int start, int end)
        {
            int n = 1;
            for (int i = start; i > end; i--, n++)
            {
                mGraphBuffer[j][i] = (mGraphBuffer[j][i] * n + points[j]) / (double)(n + 1);
            }
        }
        private void SetTextColor(Color value)
        {
            mText.ForeColor = value;
        }
        public void Write(params string[] par)
        {
            string text = mInnerText;
            foreach (string s in par)
            {
                text += s;
            }
            SetText(text);
        }
        public void WriteLine(params string[] par)
        {
            string text = mInnerText;
            if (!(text == null || text.Length == 0))
            {
                text += "\r\n";
            }
            if (par.Length > 0)
            {
                for (int i = 0; i < par.Length - 1; i++)
                {
                    text += par[i] + "\r\n";
                }
                text += par[par.Length - 1];
            }
            SetText(text);
        }
        public void WriteFirst(params string[] par)
        {
            string caption = mInnerText;
            string text = "";
            foreach (string s in par)
            {
                text = s + caption;
            }
            SetText(text);
        }
        public void WriteFirstLine(params string[] par)
        {
            string caption = mInnerText;
            string text = "";
            foreach (string s in par)
            {
                text = s + "\r\n" + caption;
            }
            SetText(text);
        }
        public void ClearText()
        {
            TextCaption = string.Empty;
        }
        #region Component Designer generated code
        private Container components = null;
        private void InitializeComponent()
        {
            this.mText = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // mText
            // 
            this.mText.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
            this.mText.BackColor = System.Drawing.Color.Transparent;
            this.mText.Location = new System.Drawing.Point(0, 0);
            this.mText.Name = "mText";
            this.mText.Size = new System.Drawing.Size(256, 256);
            this.mText.TabIndex = 0;
            this.mText.Click += new System.EventHandler(this.ControlClick);
            this.mText.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ControlMouseDown);
            // 
            // PluginGradientControl
            // 
            this.Controls.Add(this.mText);
            this.DoubleBuffered = true;
            this.Name = "PluginGradientControl";
            this.Size = new System.Drawing.Size(256, 256);
            this.Paint += new System.Windows.Forms.PaintEventHandler(this.GradientPanelControl_Paint);
            this.Leave += new System.EventHandler(this.GradientPanelControl_Leave);
            this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.GradientPanelControl_KeyUp);
            this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.GradientPanelControl_MouseDown);
            this.Enter += new System.EventHandler(this.GradientPanelControl_Enter);
            this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.GradientPanelControl_MouseUp);
            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.GradientPanelControl_KeyDown);
            this.ResumeLayout(false);

        }
        #endregion
        private bool _canUpdate  = true;
        public void BeginUpate()
        {
            lock (this)
            {
                if (_canUpdate)
                {
                    _canUpdate = false;
                }
            }
        }
        public void EndUpdate()
        {
            lock (this)
            {
                if (!_canUpdate)
                {
                    _canUpdate = true;
                }
            }
        }
    }
}
